using Microsoft.Owin.Hosting;
using Nancy;
using Owin;
using System;
using System.IO;
using System.Reflection;
using System.Text;
using System.Xml.Serialization;
using CommandLine;
using Microsoft.Owin.Extensions;
using Nancy.Hosting.Aspnet;
using ResharperKeygen;

// ReSharper disable UnusedVariable

#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously
namespace ResharperKeygen
{
    public sealed class RpcModule : Nancy.NancyModule
    {
        private readonly XmlSerializer _serializer = new XmlSerializer();
        public RpcModule()
        {
            Get("/", async (req, resp) =>
            {
                return Response.AsText("welcome :)", "text/plain");
            });

            //{http://jet-lic.pedoc.cn:6080/rpc/obtainTicket.action?oldLicenseServerUrl=ObtainTicketRequest%3A%3AOldLicenseServerUrl%3D&obtainTicketActionName=obtainTicket.action&productFamilyId=59BB7CF0-D203-4E54-9A5F-04FBB1AEBCD4&edition=Full&version=20220802&versionNumber=2022200&buildDate=20220802&hostName=DESKTOP-HOME.&userName=pedoc&salt=11443765&machineId=ac74e52a-d271-45e9-92b3-42771e5bf75c&clientVersion=13&buildNumber=2022.2.222.0.20220728.74125}
            Get("/rpc/obtainTicket.action", async (req, resp) =>
            {
                var oldLicenseServerUrl = GetQueryParameter("oldLicenseServerUrl", string.Empty);
                var obtainTicketActionName = GetQueryParameter("obtainTicketActionName", string.Empty);
                var productFamilyId = GetQueryParameter("productFamilyId", string.Empty);
                var edition = GetQueryParameter("edition", string.Empty);
                var version = GetQueryParameter("version", string.Empty);
                var versionNumber = GetQueryParameter("versionNumber", string.Empty);
                var buildDate = GetQueryParameter("buildDate", string.Empty);
                var hostName = GetQueryParameter("hostName", string.Empty);
                var userName = GetQueryParameter("userName", string.Empty);
                var salt = GetQueryParameter<long>("salt", 0);
                var machineId = GetQueryParameter("machineId", string.Empty);
                var clientVersion = GetQueryParameter("clientVersion", string.Empty);
                var buildNumber = GetQueryParameter("buildNumber", string.Empty);
                Console.WriteLine($"Request from {Request.UserHostAddress},Url={Request.Url}");
                var xml = _serializer.SerializeObject(new ObtainTicketResponse()
                {
                    Salt = salt
                });
                return Response.AsText(xml, "application/xml");
            });

            //"http://jet-lic.pedoc.cn:6080/rpc/ping.action?hostName=%44%45%53%4B%54%4F%50%2D%48%4F%4D%45%2E&userName=pedoc&salt=12%39457%396&machineId=ac74e52a%2Dd271%2D45e%39%2D%392b3%2D42771e5bf75c&clientVersion=13&buildNumber=2022%2E2%2E222%2E0%2E2022072%38%2E74125"
            Get("/rpc/ping.action", async (req, resp) =>
            {
                try
                {
                    var salt = GetQueryParameter<long>("salt", 0);
                    var machineId = GetQueryParameter("machineId", string.Empty);
                    var hostName = GetQueryParameter("hostName", string.Empty);
                    var userName = GetQueryParameter("userName", string.Empty);
                    var authorizationHeader = GetQueryParameter("authorizationHeader", string.Empty);
                    Console.WriteLine($"Request from {Request.UserHostAddress},Url={Request.Url}");
                    var xml = _serializer.SerializeObject(new PingResponse()
                    {
                        Salt = salt
                    });
                    return Response.AsText(xml, "application/xml");
                }
                catch (Exception ex)
                {
                    return Response.AsText("", "application/xml");
                }
            });
        }

        private T GetQueryParameter<T>(string key, T defaultValue = default(T))
        {
            var value = Request.Query[key];
            if (value != null) return (T)value;
            return defaultValue;
        }
    }
}

public class XmlSerializer
{
    public static Encoding Encoding = Encoding.UTF8;

    public string SerializeObject(object value)
    {
        using var stream = new MemoryStream();
        var xml = new System.Xml.Serialization.XmlSerializer(value.GetType());
        var xns = new XmlSerializerNamespaces();
        xns.Add("", "");
        xml.Serialize(stream, value, xns);
        return Encoding.GetString(stream.ToArray());
    }

    public T DeserializeObject<T>(string value)
    {
        using var stream = new MemoryStream(Encoding.GetBytes(value));
        using var sr = new StreamReader(stream, Encoding);

        var xmlSerializer = new System.Xml.Serialization.XmlSerializer(typeof(T));
        return (T)xmlSerializer.Deserialize(sr);
    }
}

// ReSharper disable once UnusedMember.Global
public class Bootstrapper : DefaultNancyBootstrapper
{
    protected override IRootPathProvider RootPathProvider => new AspNetRootPathProvider();
}

public class Startup
{
    public void Configuration(IAppBuilder app)
    {
        app.UseNancy();
        app.UseStageMarker(PipelineStage.MapHandler);
    }
}

public class Program
{
    public static void Main(string[] args)
    {
        var assemblyName = Assembly.GetExecutingAssembly().GetName();
        Console.Title = $"ResharperKeygen for 2022.2+,Version:{assemblyName.Version}";

        // Patcher.MatchByPattern();
        // return;

        var parserResult = Parser.Default.ParseArguments<CliOptions>(args);
        var cliOptions = parserResult?.Value;
        if (cliOptions == null || !cliOptions.IsValid())
            cliOptions = CliOptions.GetDefault();

        if (cliOptions.RunPatch)
        {
            try
            {
                Patcher.PatchByPattern();
            }
            catch (Exception ex)
            {
                Console.WriteLine("file patch failed,exception:" + ex.Message);
            }
        }
        else
        {
            Console.WriteLine("Skipped autorun patch");
        }

        string url = cliOptions.ListenAddress;
        WebApp.Start<Startup>(url);
        Console.WriteLine($"Fake license server running on {url},Enter q to exit");

        while (Console.ReadKey().Key != ConsoleKey.Q)
        {
            Console.WriteLine("Enter q to exit");
        }
    }
}
